/**
 * webpack配置文件
 * 优化方法1、
 * 优化：HMR:hot module replacement  热模块替换/  作用 ：一个模块发生变化，只会重新打包这个模块而不是打包所有模块，极大提高打包速度 devServer{hot:true}
 * 样式文件：直接就可以用hmr功能；因为style-loader内部实现了（style-loader在开发环境下使用，生产环境下把css抽离出来提供性能用MiniCssExtractPlugin.loader这时候就没有HMR功能）
 * js文件：默认没有hmr功能 -->修改js代码，添加支持hmr功能的代码，（z注意：只能处理非入口文件的其他js文件，入口文件一旦发生变化必定全部重新打包）
 * html:默认不能使用hmr功能，同时html文件不能热跟新（解决：修改entry入口，将html文件引入;此时改动整个html全部更新，不能使用hmr功能，和js有本质区别）
 * 
 */
const {resolve} = require('path') ;
const HtmlWebpackPlugin = require('html-webpack-plugin');

const MiniCssExtractPlugin = require('mini-css-extract-plugin') 
//设置node环境变量  让browserlist为开发环境
process.env_NODE_ENV = "development";
/**
 * 项目优化5：pwa :渐进式网络开发应用程序（离线可访问），依赖于workbox库 用插件workbox-webpack
 */
 const WorkboxWebpackPlugin = require('workbox-webpack-plugin') 

/**
 * 优化方法3： tree shaking:去除无用代码
 * 前提：1、必须使用es6模块化 2、开启production环境（作用：减少代码体积）
 * 在package.json中配置 "sideEffects":false(所有代码都可以进行 tree shaking)  问题：可能会把css  @babel/polyfill文件干掉
 * 解决 "sideEffects":["*.css"]
 */
/**
 * y优化4：code split 代码分割
 *     optimization:{
        splitChunks:{
            chunks:'all'
        }
    },
 */
// optimize-css-assets-webpack-plugin  //压缩css
const OptimizeCssAssetsWbpackPlugin = require('optimize-css-assets-webpack-plugin')
 module.exports ={
     entry:['./src/index.js','./src/index.html'],
    /**
     * 可以将node_modules中的代码单独打包一个chunK最终输出，
     * 自动分析多入口chunk,有没有公共的文件，如果有就打包成一个单独文件chunk，防止重复打包
     */
    optimization:{
        splitChunks:{
            chunks:'all'
        }
    },
     //多入口：
    //  entry:{
    //      main:'./src/index.js',
    //      print:'./src/print.js'
    //  },
     output:{
         //输出名字 加一个后缀防止强缓存下代码更新不起效
        //  filename:'build.[contenthash:10].js',
        //[name]对应入口取的名字：main print 
         filename:'[name].[contenthash:10].js',
         //输出路径
         //—__dirname 当前项目根路径
         path:resolve(__dirname,'build')
     },
     plugins:[
        //插件配置
        //html-webpack-plugin 
        //功能：默认创建一个空的HTML,自动引入打包输出的所有资源
        //需求：需要有结构的html文件
        new HtmlWebpackPlugin(
            {
                //复制'./src/index.html',并且引入打包输出所哟有资源
                template:'./src/index.html',
                //压缩html,
                minify:{
                    //移除空格,也就是压缩html
                    collapseWhitespace:true,
                    //移除注释
                    removeComments:true
                }
            }
        ),
        //抽离CSS到css/build下
        new MiniCssExtractPlugin({
            filename:'css/build.[contenthash:10].css'
        }),
        //压缩css
        new OptimizeCssAssetsWbpackPlugin(),
        //离线访问
        new WorkboxWebpackPlugin.GenerateSW({
            /**
             * 1\帮助serviceworker快速启动，2、删除旧的servicrworker
             * 功能：生成 serviceworker，在入口文件配置
             */
            clientsClaim:true,
            skipWaiting:true
        })

     ],
    
     module:{
         rules:[
             //oneOd 以下loader只能被处理一次，提高效率
             {
                 oneOf:[
                                //配置loader
                    {
                        //匹配CSS结尾的
                        test:/\.css$/,
                        use:[
                            //use中loader的顺序，冲右到左 一次执行
                            //创建style 标签，将JS中的眼视光hi资源插入，添加到head中生效
                        //    'style-loader',
                        //用法二：提取CSS 成单独文件，这个loader取代style-loader,这样生成的css右link标签自动引入，不会产生style(styel-loader的css文件混合在build.JS中的，导致输出的bundle文件太大)
                        MiniCssExtractPlugin.loader,   
                            //将css文件变成commonjs模块加载到js中，里面内容是样式字符串
                            'css-loader',
                            /**
                             * css兼容处理：post-css -->postcss-loader postcss-preset-env
                             */
                            //'postcss-loader' 这是默认配置  等于 {loader：'postcss-loader'} 如果要自行配置就如下：
                            {
                                loader:'postcss-loader',
                                options:{
                                    ident:'postcss',
                                    plugins:()=>[
                                        //postcss插件;这个插件帮助POSTCSS找到package.json中browserlist里面的配置，通过配置加载指定的css兼容样式，要在pakege.json中去配置规则
                                        /**
                                         * 如果要改成开发环境，-->设置node环境变量 ：process.env_NODE_ENV = "development"
                                            "browserslist":{
                                            "development":[
                                                "last 1 chrome version", //兼容最近的chrome版本浏览器
                                                "last 1 firefox version",
                                                "last 1 safari version",
                                            ],
                                            默认是生产环境
                                            "production":[
                                                ">0.2%",//兼容大于99.8%的浏览器
                                                "not dead", //没有死的浏览器
                                                "not op_mini all"
                                            ]
                                            }
                                        */
                                        require('postcss-preset-env')()
                                    ]
                                }
                            },
                            'less-loader'
                        ]
            
                    },
                    {
                        //不能处理html中的图片src
                        test:/\.(jpg|png|gif)$/,
                        //使用多个loader的时候用use:[]
                        //url-loader依赖file-loader 所以这时候需要下载两个依赖
                        loader:'url-loader',
                        options:{
                            //图片小于15kb处理base64
                            //优点 减少请求（减轻服务器压力）
                            //缺点：图片体积会更大
                            limit:15*1024,
                            //问题url-loader默认使用es6模块化解析，html-loader引入图片是commonjs
                            //解析会出现src=[object Module]
                            //解决：关闭url-loader的es6模块化，使用commonjs解析
                            esModule:false,
                            //取生成图片has值的前十位，[ext]原来的扩展名
                            name:'[hash:10].[ext]'
                        }
                    },
                    {
                        test:/\.html$/,
                        //处理HTML中的img(负责引入IMG从而被url-loader处理)
                        loader:'html-loader'
                    },
                    //打包其他资源 除了（html.js,css资源以外的其他资源）
                    {
                        exclude:/\.(css|js|html|jpg|png|gif|json)$/,
                        loader:'file-loader'

                    },
                    //语法检查 eslint-loader eslint eslint-loader依赖于eslint库,(只检查自己的代码第三方不检查) 设置检查规则：
                    //package.json 中的eslintConfig中设置
                    // airbnb --> eslint-config-airbnb-base(用这个库的规则还需要依赖 eslint-plugin-import)
                    /**
                     *   "eslintConfig":{
                                "extends":"airbnb-base"
                            }
                    */
                    {
                        test:/\.js$/,
                        exclude:/node_modules/,
                        use:[
                            //一定要先做eslint,在做babel处理
                            // {
                            //     loader:'eslint-loader',
                            //     options:{
                            //         //自动修复错误 规范代码
                            //         fix:true
                            //     }
                            // },
                            /**
                            *             
                            * js兼容处理：babel-loader @babel/core @babel/preset-env 处理有些浏览器不能识别es6比如IE不能识别箭头函数
                            * 1、基本的js兼容处理 --> @babel/preset-env,(promise就不能转换)
                            * 2、全部的js兼容处理 --> @babel/polyfill,使用方法，在js文件中 import ”@babel/polifill“ ; 就可以了
                            * 问题：只需要解决部分兼容问题，解决所有兼容 体积太大了  不好
                            * 3、需要做兼容性处理的就做按需加载，加载指定的兼容库就可以了 -->  core-js
                                第一个方法和第三个结合解决问题最佳
                            */
                            {
                                loader:'babel-loader',
                                options:{
                                //预设：只是babel做怎么样的兼容处理
                                    presets:[
                                        [
                                            '@babel/preset-env',
                                            {
                                                //按需加载
                                                useBuiltIns:'usage',
                                                //指定core-js版本
                                                corejs:{
                                                    version:3
                                                },
                                                //兼容性做到那个版本的浏览器
                                                targets:{
                                                    chrome:'60',
                                                    firefox:'60',
                                                    ie:'9'
                                                }

                                            }
                                        ]
                                    ],
                                    //由于HMR是基于dev-server所以只能在开发环境使用，生产环境不能使用hmr，所以开启babel缓存，没变的文件就不用重新打包,第二次构建才读取缓存，速度更快

                                    /**
                                     * 优化方法2：
                                     * babel缓存：-->让第二次打包跟很快
                                     * 文件资源缓存：
                                     * hash:因为JS和css同时使用一个hsah值,如果重新打包，会导致所欲哦缓存失效（之改动一个文件导致所有缓存失效，解决方法使用chunkhash）
                                     * chunkhash:根据chunk生成hash值，如果打包源于同一个chunk，那么hash值就一样，由于css和js属于同一个chunk所以哈希值也一样，缓存无效
                                     * contenthash:根据文件内容生成，不同文件哈希值不一样  所以缓存有效
                                     * -->让线上运行缓存更好使用 reSOURCE-map
                                     */
                                    cacheDirectory:true
                                }
                            } 
                        ],
                    }
                 ]
             }
      

         ]
     },
     //生产环境会自动压缩js代码
     mode:'production',
     //开发服务器，用来热跟新，特点：只会在内存中编译打包，不会输出，如：vue,npx  webpack-dev-server (npx启动找本地 )（npm i  webpack-dev-server -D）
     devServer:{
        contentBase:resolve(__dirname,'build'),
        //启动gzip压缩
        compress:true,
        port:3000,
        open:true,
        //开启热模块替换，更改后需要重新 启动 npx webpack-dev-server
        hot:true
     },
     /**
      * source-map : （外部）一种提供源代码到构建后代码映射技术 （如果构建后代码出错了，可以通过映射追踪源代码错误）(错误代码的准确信息和位置)
      * [inline-|hidden-|eval-][nosources-][cheap-[module-]]  source-map
      * inline-source-map:内联 （只生成一个总的source-map）
      * hidden-source-map:外部 （能提示文件错误，提示构建后错误代码的位置，不能追踪到源代码的错误位置。可以隐藏源代码提高安全）
      * eval-source-map:内联(每一个文件都对应生产wource-map,都在eval)
      * cheap-source-map:外部 准确找到错误信息和位置 只能精确到行
      * 内联和外联区别：1、外部生了文件，内联没有。2、内联构建速度快
      * 1、开发环境：速度快，调试更加友好， 速度快->(eval>inline>cheap  可以组合eval-cheap-source-map最快组合，
      * 如果调试更好 cheap-module-source-map cheap=module-source-map mudule会将loader里面的suorce-map也加进来) 综合eval-source-map快也精确
      * 2、生产环境：源代码要不要隐藏，调试要不要更友好 hidden-source-map nosource-suorce-map(内联会让代码体积庞大，所以生产环境都不要内联)
      * 开发和生产都比较好的用source-map
      */
     devtool:'source-map',
     /**
      * 优化6：extenals 是某些文件不被打包进来，比如cdn引入的jquery
      */
     externals:{
         //拒绝jQuery被打包进来。要用必须在html中把库引进来
         jquery:'jQuery'
     }
     /**
      * dll 单独开启webpack.dll。js
      */

 }